package com.csx.springcloud.invoke.openfeign.consumer.base.loadbalancer;

import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.core.env.Environment;
import reactor.core.publisher.Mono;

/**
 * 该类主要是 实现根据用户的配置动态切换 负载均衡器
 * @author csx
 * @data 2023年7月22日11:19:47
 */
public class CustomDynamicLoadBalancer implements ReactorServiceInstanceLoadBalancer {

    private final Logger logger = LoggerFactory.getLogger(CustomDynamicLoadBalancer.class);
    private static final LoadStrategy DEFAULT_LOAD_STRATEGY = LoadStrategy.ROUND_ROBIN;

    private final Environment environment;

    public CustomDynamicLoadBalancer(Environment environment) {
        this.environment = environment;
    }

    @Getter
    enum LoadStrategy {

        RANDOM("random", "随机负载"),

        ROUND_ROBIN("round_robin", "轮询负载");


        private final String name;

        private final String desc;

        // 构造函数
        LoadStrategy(String name, String desc) {
            this.name = name;
            this.desc = desc;
        }
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ReactorServiceInstanceLoadBalancer loadBalancerClient = getLoadBalancer();
        return loadBalancerClient.choose(request);
    }

    @Override
    public Mono<Response<ServiceInstance>> choose() {
        ReactorServiceInstanceLoadBalancer loadBalancer = getLoadBalancer();
        return loadBalancer.choose();
    }

    private ReactorServiceInstanceLoadBalancer getLoadBalancer() {

        ReactorServiceInstanceLoadBalancer targetLoadBalancer = null;

        // TODO 最优的方法是创建一个封装了策略的 @ConfigurationProperties 注解标注的类,然后实现配置动态刷新,在这里注入进来即可
        String loadStrategy = environment.getProperty("loadbalancer.loadStrategy", String.class, DEFAULT_LOAD_STRATEGY.getName());
        LoadStrategy applyLoadStrategy = DEFAULT_LOAD_STRATEGY;
        for (LoadStrategy value : LoadStrategy.values()) {
            if (value.getName().equals(loadStrategy)) {
                applyLoadStrategy = value;
            }
        }

        targetLoadBalancer = CustomLoadBalancerFactory.createLoadBalancer(applyLoadStrategy);

        if(logger.isDebugEnabled()) {
            logger.debug("The loadBalancer actually used by the dynamicLoadBalancer is {}", targetLoadBalancer);
        }

        return targetLoadBalancer;


    }
}

